#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <ctype.h>
#include <signal.h>

// 默认日志文件
#define LOG_FILE "frp_deamon.log"
#define MAX_LOG_SIZE 10485760  // 10 MB
#define TRIM_SIZE 1048576     // 1 MB to delete from the start
int g_log_quiet = 0;

// 默认监听间隔时间
#define DEFAULT_INTERVAL 5

// 显示帮助信息
void show_help() {
    printf("Usage: frp_deamon [options]\n");
    printf("Options:\n");
    printf("  -cc <frpc_config>    Specify the configuration files for frpc (default: frpc.ini). You can specify multiple files separated by commas.\n");
    printf("                       If 'no' is passed, no frpc processes will be started.\n");
    printf("  -cs <frps_config>    Specify the configuration files for frps (default: frps.ini). You can specify multiple files separated by commas.\n");
    printf("                       If 'no' is passed, no frps processes will be started.\n");
    printf("  -q                   Quiet mode. Only log to file, no output to stdout.\n");
    printf("  -t <interval>        Set the interval time in seconds for checking processes (default: 5 seconds). Minimum value is 1.\n");
    printf("  -h                   Show this help message and exit.\n");
    printf("\nDescription:\n");
    printf("  frp_deamon is a background process monitoring tool for FRP (Fast Reverse Proxy).\n");
    printf("  It checks whether the FRPC (client) and FRPS (server) processes are running on the specified ports.\n");
    printf("  If the processes are not running, it will start them using the provided configuration files.\n");
    printf("  This tool ensures that the necessary components of FRP are always running to maintain connectivity.\n");
    printf("\n  Author: amuliang\n");
    printf("\nExample:\n");
    printf("  ./frp_deamon -cc frpc1.ini,frpc2.ini -cs frps1.ini,frps2.ini\n");
    printf("  ./frp_deamon (uses default frpc.ini and frps.ini)\n");
    printf("  ./frp_deamon -cc no -cs frps1.ini\n");
}

// 获取当前时间作为日志的时间戳
void get_current_time(char *buffer, size_t size) {
    time_t now = time(NULL);
    struct tm *tm_info = localtime(&now);
    strftime(buffer, size, "%Y-%m-%d %H:%M:%S", tm_info);
}

// 写入日志文件，同时控制是否输出到标准输出
void log_message(const char *message) {
    FILE *logfile = fopen(LOG_FILE, "a"); // 打开日志文件，追加模式
    if (logfile == NULL) {
        perror("fopen");
        return;
    }

    char timestamp[20];
    get_current_time(timestamp, sizeof(timestamp));
    
    // 格式化日志消息
    char log_msg[512];
    snprintf(log_msg, sizeof(log_msg), "[%s] %s\n", timestamp, message);

    // 检查日志文件大小并裁剪
    fseek(logfile, 0, SEEK_END);
    long file_size = ftell(logfile);
    if (file_size > MAX_LOG_SIZE) {
        // 文件过大，裁剪前面 1MB
        fseek(logfile, TRIM_SIZE, SEEK_SET);
        FILE *temp_log = fopen("temp_log.log", "w");
        if (temp_log) {
            char buffer[1024];
            while (fgets(buffer, sizeof(buffer), logfile)) {
                fputs(buffer, temp_log);
            }
            fclose(temp_log);
            remove(LOG_FILE);
            rename("temp_log.log", LOG_FILE);
            logfile = fopen(LOG_FILE, "a");  // 重新打开文件
        } else {
            perror("Error opening temporary log file");
            return;
        }
    }
    
    // 打印到日志文件
    fprintf(logfile, "%s", log_msg);
    fflush(logfile); // 确保每次写入日志都会立即保存
    fclose(logfile); // 关闭日志文件

    // 如果不是安静模式，打印到标准输出
    if (!g_log_quiet) {
        printf("%s", log_msg);
    }
}

// 从配置文件中读取指定的键值
int read_port_from_ini(const char *file_path, const char *key) {
    FILE *file = fopen(file_path, "r");
    if (!file) {
        perror("fopen");
        return -1; // 打开文件失败，返回 -1 表示出错
    }

    char line[256];
    int port = -1;

    while (fgets(line, sizeof(line), file)) {
        if (strncmp(line, key, strlen(key)) == 0) {
            sscanf(line, "%*[^=]=%d", &port);
            break;
        }
    }

    fclose(file);
    return port;
}

int read_value_from_ini(const char *file_path, const char *key, char *value, size_t value_size) {
    FILE *file = fopen(file_path, "r");
    if (!file) {
        perror("fopen");
        return -1; // 打开文件失败，返回 -1 表示出错
    }

    char line[256];
    int found = 0;

    while (fgets(line, sizeof(line), file)) {
        if (strncmp(line, key, strlen(key)) == 0) {
            sscanf(line, "%*[^=]=%s", value);
            found = 1;
            break;
        }
    }

    fclose(file);
    return found ? 0 : -1; // 如果找到，返回 0，否则返回 -1
}

// 检查指定端口是否被使用
int is_port_in_use(int port, const char *type) {
    char command[128];
    char result[256];
    FILE *fp;

    // 构建 netstat 命令以检查端口
    snprintf(command, sizeof(command),
             "netstat -anp | grep %d | grep %s", port, type);

    // 执行命令
    fp = popen(command, "r");
    if (fp == NULL) {
        perror("popen");
        return 0; // 如果命令失败，认为端口未被使用
    }

    // 检查命令输出
    if (fgets(result, sizeof(result), fp) != NULL) {
        pclose(fp);
        return 1; // 端口正在被使用
    }

    pclose(fp);
    return 0; // 端口未被使用
}

// 检查某个命令是否正在运行，存在则返回PID，不存在返回0
int get_command_pid(const char *command) {
    DIR *proc_dir = opendir("/proc");
    if (proc_dir == NULL) {
        perror("opendir");
        return 0; // 打开 /proc 目录失败
    }

    struct dirent *entry;
    while ((entry = readdir(proc_dir)) != NULL) {
        // 只处理以数字开头的目录（即进程ID）
        if (!isdigit(entry->d_name[0])) {
            continue;
        }

        char cmdline_path[256];
        snprintf(cmdline_path, sizeof(cmdline_path), "/proc/%s/cmdline", entry->d_name);

        FILE *cmdline_file = fopen(cmdline_path, "r");
        if (cmdline_file == NULL) {
            continue; // 无法打开 cmdline 文件，可能是系统进程，跳过
        }

        char cmdline[1024];
        if (fgets(cmdline, sizeof(cmdline), cmdline_file) != NULL) {
            // cmdline 文件包含命令行参数，用 '\0' 分隔
            // 检查第一个参数是否包含目标命令
            if (strstr(cmdline, command) != NULL) {
                fclose(cmdline_file);
                closedir(proc_dir);
                return atoi(entry->d_name); // 返回进程ID
            }
        }

        fclose(cmdline_file);
    }

    closedir(proc_dir);
    return 0; // 未找到匹配的命令
}

// 杀掉指定PID的进程
int kill_process_by_pid(int pid) {
    if (kill(pid, SIGKILL) == 0) {
        printf("Successfully killed process with PID: %d\n", pid);
        return 1; // 成功杀掉进程
    } else {
        perror("kill");
        return 0; // 杀进程失败
    }
}

// 检查远程服务端口是否启用。这里可以通过建立一个 TCP 连接来测试远程端口是否可用：
int is_remote_port_accessible(const char *host, int port) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("socket");
        return 0; // 无法创建 socket，返回不可访问
    }

    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);

    // 将主机名转换为 IP 地址
    if (inet_pton(AF_INET, host, &server_addr.sin_addr) <= 0) {
        perror("inet_pton");
        close(sock);
        return 0; // 无效的地址，返回不可访问
    }

    // 尝试连接
    int result = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
    close(sock); // 关闭 socket

    return (result == 0); // 如果连接成功返回 1，否则返回 0
}

// 启用进程并在后台执行
void start_daemon_process(const char *command) {
    // 在命令后加上 "&" 使其在后台运行
    char cmd[256];
    snprintf(cmd, sizeof(cmd), "%s &", command);

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程
        if (setsid() == -1) { // 创建新会话，确保进程独立运行
            perror("setsid");
            exit(EXIT_FAILURE);
        }

        // 执行命令
        execl("/bin/sh", "sh", "-c", cmd, NULL);

        // 如果 execl 返回，说明执行失败
        perror("execl");
        exit(EXIT_FAILURE);
    }

    // 父进程直接返回，继续执行其他任务

    // 使用 popen 执行命令，并将输出重定向到标准输出
    // FILE *fp = popen(cmd, "r");
    // if (fp == NULL) {
    //     perror("popen");
    //     return;
    // }

    // 读取并打印输出（上面命令加了 &> /dev/null 后应该是没有输出了）
    // char output[128];
    // while (fgets(output, sizeof(output), fp) != NULL) {
    //     printf("%s", output);
    //     log_message(output);
    // }
    // fclose(fp);  // 关闭文件指针

    char message[256];
    snprintf(message, sizeof(message), "command exec finished： %s", command);
    printf(message);
    log_message(message);

}

// 分割字符串并返回数组
int split_string(char *str, char *delimiter, char **result, int max_results) {
    int count = 0;
    char *token = strtok(str, delimiter);
    while (token != NULL && count < max_results) {
        result[count++] = token;
        token = strtok(NULL, delimiter);
    }
    return count;
}

int main(int argc, char *argv[]) {
    // 默认配置文件路径
    char frpc_config[128] = "frpc.ini";
    char frps_config[128] = "frps.ini";
    int interval = DEFAULT_INTERVAL;  // 默认间隔时间为 5 秒

    char *frpc_files[10]; // 用于存储 frpc 配置文件的数组
    char *frps_files[10]; // 用于存储 frps 配置文件的数组
    int frpc_file_count = 0;
    int frps_file_count = 0;
    int no_frpc_file = 0;
    int no_frps_file = 0;

    // 解析命令行参数
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-cc") == 0 && i + 1 < argc) {
            if (strcmp(argv[++i], "no") != 0) {
                frpc_file_count = split_string(argv[i], ",", frpc_files, 10); // 分割配置文件
            }else {
                no_frpc_file = 1;
            }
        } else if (strcmp(argv[i], "-cs") == 0 && i + 1 < argc) {
            if (strcmp(argv[++i], "no") != 0) {
                frps_file_count = split_string(argv[i], ",", frps_files, 10); // 分割配置文件
            }else {
                no_frps_file = 1;
            }
        } else if (strcmp(argv[i], "-q") == 0) {
            g_log_quiet = 1; // 启用安静模式
        } else if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
            int user_interval = atoi(argv[++i]);
            if (user_interval < 1) {
                fprintf(stderr, "Invalid interval. Minimum interval is 1 second.\n");
                return 1; // 无效的间隔时间，退出程序
            } else {
                interval = user_interval;
            }
        } else if (strcmp(argv[i], "-h") == 0) {
            show_help();
            return 0; // 显示帮助后退出程序
        } else {
            fprintf(stderr, "Unknown option: %s\n", argv[i]);
            show_help();
            return 1; // 未知参数，显示帮助并退出
        }
    }

    // 如果没有指定配置文件，使用默认文件
    if (no_frpc_file == 0 && frpc_file_count == 0) {
        frpc_file_count = 1;
        frpc_files[0] = frpc_config; // 默认 frpc.ini
    }
    if (no_frps_file == 0 && frps_file_count == 0) {
        frps_file_count = 1;
        frps_files[0] = frps_config; // 默认 frps.ini
    }

    // 写入启动日志
    log_message("FRP Deamon started.");

    while (1) {
        // 检查每个 frpc 配置文件
        for (int i = 0; i < frpc_file_count; i++) {
            char message[256];
            char server_addr[128];
            if (read_value_from_ini(frpc_files[i], "server_addr", server_addr, sizeof(server_addr)) == -1) {
                snprintf(message, sizeof(message), "Failed to read server_addr from %s.", frpc_files[i]);
                log_message(message);
                continue;
            }
            int server_port = read_port_from_ini(frpc_files[i], "server_port");
            int frpc_port = read_port_from_ini(frpc_files[i], "remote_port");
            if (server_port == -1) {
                snprintf(message, sizeof(message), "Failed to read server_port from %s.", frpc_files[i]);
                log_message(message);
                continue;
            }
            if (frpc_port == -1) {
                snprintf(message, sizeof(message), "Failed to read remote_port from %s.", frpc_files[i]);
                log_message(message);
                continue;
            }
            // 检查 frpc 是否运行
            if (!is_remote_port_accessible(server_addr, server_port)) {
                snprintf(message, sizeof(message), "frps server (%s:%d) not accessible. No need to restart frpc.", server_addr, server_port);
                log_message(message);
                continue;
            }
            // if (!is_port_in_use(frpc_port, "ESTABLISHED")) {
            if (!is_remote_port_accessible(server_addr, frpc_port)) {
                snprintf(message, sizeof(message), "frpc server (%s:%d) not accessible. Starting frpc...", server_addr, frpc_port);
                log_message(message);
                char frpc_command[256];
                snprintf(frpc_command, sizeof(frpc_command), "./frpc -c %s", frpc_files[i]);
                int pid = get_command_pid(frpc_command);
                if(pid) { // 如果存在了一个相同命令的进程，则杀掉
                    snprintf(message, sizeof(message), "A process running the command '%s' already exists with PID %d. Attempting to kill it.", frpc_command, pid);
                    log_message(message);
                    if(!kill_process_by_pid(pid)) {
                        snprintf(message, sizeof(message), "Could not kill the process running command '%s' with PID: %d", frpc_command, pid);
                        log_message(message);
                    }
                }
                start_daemon_process(frpc_command);  // 启动守护进程
            } else {
                snprintf(message, sizeof(message), "frpc server (%s:%d) is accessible. No need to restart frpc.", server_addr, frpc_port);
                log_message(message);
            }
        }

        // 检查每个 frps 配置文件
        for (int i = 0; i < frps_file_count; i++) {
            char message[256];
            int frps_port = read_port_from_ini(frps_files[i], "bind_port");
            if (frps_port == -1) {
                snprintf(message, sizeof(message), "Failed to read bind_port from %s.", frps_files[i]);
                log_message(message);
                continue;
            }
            // 检查 frps 是否运行
            if (!is_port_in_use(frps_port, "LISTEN")) {
                snprintf(message, sizeof(message), "frps not running on port %d. Starting it...", frps_port);
                log_message(message);
                char frps_command[256];
                snprintf(frps_command, sizeof(frps_command), "./frps -c %s", frps_files[i]);
                int pid = get_command_pid(frps_command);
                if(pid) { // 如果存在了一个相同命令的进程，则杀掉
                    snprintf(message, sizeof(message), "A process running the command '%s' already exists with PID %d. Attempting to kill it.", frps_command, pid);
                    log_message(message);
                    if(!kill_process_by_pid(pid)) {
                        snprintf(message, sizeof(message), "Could not kill the process running command '%s' with PID: %d", frps_command, pid);
                        log_message(message);
                    }
                }
                start_daemon_process(frps_command);  // 启动守护进程
            } else {
                snprintf(message, sizeof(message), "frps is running on port %d.", frps_port);
                log_message(message);
            }
        }

        // 根据用户设置的间隔时间进行休眠
        sleep(interval);
    }

    // 写入结束日志
    log_message("FRP Deamon stopped.");

    return 0;
}
